# Copyright 2008, 2009 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# GNU Radio is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Radio; see the file COPYING.  If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#

from gnuradio import gr, digital
from gnuradio.digital import packet_utils
import gnuradio.gr.gr_threading as _threading
from numpy.random import random

##payload length in bytes
DEFAULT_PAYLOAD_LEN = 512

##how many messages in a queue
DEFAULT_MSGQ_LIMIT = 2

##threshold for unmaking packets
DEFAULT_THRESHOLD = 12

##################################################
## Options Class for OFDM
##################################################
class options(object):
	def __init__(self, **kwargs):
		for key, value in kwargs.iteritems(): setattr(self, key, value)

##################################################
## Packet Encoder
##################################################
class _packet_encoder_thread(_threading.Thread):

	def __init__(self, msgq, payload_length, send):
		#print 'init packet thread'
		self._msgq = msgq
		self._payload_length = payload_length
		self._send = send
		_threading.Thread.__init__(self)
		self.setDaemon(1)
		self.keep_running = True
		self.start()

	def run(self):
		sample = '' #residual sample
		while self.keep_running:
			#print self._msgq.count(), len(sample)
			msg = self._msgq.delete_head() #blocking read of message queue
			sample = sample + msg.to_string() #get the body of the msg as a string
			while len(sample) >= self._payload_length:
				payload = sample[:self._payload_length]
				sample = sample[self._payload_length:]
				self._send(payload)

class packet_encoder(gr.hier_block2):
	"""
	Hierarchical block for wrapping packet-based modulators.
	"""

	state = ''
	def __init__(self, access_code='', preamble_size=0):
		"""
		packet_mod constructor.
		@param access_code AKA sync vector
		@param payload_length number of bytes in a data-stream slice
		"""
		self.preamble_size = preamble_size
		self.state = "SEND_PREAMBLE"
		#print 'Init Packet Encoder'
		#setup parameters
		if not access_code: #get access code
			access_code = packet_utils.default_access_code
		if not packet_utils.is_1_0_string(access_code):
			raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)

		self._access_code = access_code
		#create blocks
		msg_source = gr.message_source(gr.sizeof_char, DEFAULT_MSGQ_LIMIT)
		self._msgq_out = msg_source.msgq()
		#initialize hier2
		gr.hier_block2.__init__(
			self,
			"packet_encoder",
			gr.io_signature(0, 0, 0), # Input signature
			gr.io_signature(1, 1, gr.sizeof_char) # Output signature
		)
		#connect
		self.connect(msg_source, self)

	def send_pkt(self, payload):
		"""
		Wrap the payload in a packet and push onto the message queue.
		@param payload string, data to send
		"""

		if self.state == "SEND_PREAMBLE":
			#print self.state
			preamble = ''
			for i in range(self.preamble_size):
				preamble += chr(0)
			(packed_access_code, padded) = packet_utils.conv_1_0_string_to_packed_binary_string(self._access_code)
			packet = preamble + packed_access_code + payload
			msg = gr.message_from_string(packet)
			self._msgq_out.insert_tail(msg)
			self.state = "SEND_PACKET"
		elif self.state == "SEND_PACKET":
			# call packet creation from here
			(packed_access_code, padded) = packet_utils.conv_1_0_string_to_packed_binary_string(self._access_code)
			packet = packed_access_code + payload
			#print "\t\tsending", [ ord(i) for i in packet]
			#print packet
			msg = gr.message_from_string(packet)
			self._msgq_out.insert_tail(msg)

##################################################
## Packet Decoder
##################################################
class _packet_decoder_thread(_threading.Thread):

	def __init__(self, msgq, callback):
		#print 'init packet thread'
		_threading.Thread.__init__(self)
		self.setDaemon(1)
		self._msgq = msgq
		self.callback = callback
		self.keep_running = True
		self.start()

	def run(self):
		while self.keep_running:
			#print 'running', self._msgq.count()
			msg = self._msgq.delete_head()
			payload = msg.to_string()
			#ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1()))
			#print "\t received: ", payload
			if self.callback:
				self.callback(True, payload)

class packet_decoder(gr.hier_block2):
	"""
	Hierarchical block for wrapping packet-based demodulators.
	"""

	def __init__(self, access_code='', threshold=-1, callback=None):
		"""
		packet_demod constructor.
		@param access_code AKA sync vector
		@param threshold detect access_code with up to threshold bits wrong (0 -> use default)
		@param callback a function of args: ok, payload
		"""
		#access code
		if not access_code: #get access code
			access_code = packet_utils.default_access_code
		if not packet_utils.is_1_0_string(access_code):
			raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,)
		self._access_code = access_code
		#threshold
		if threshold < 0: threshold = DEFAULT_THRESHOLD
		self._threshold = threshold
		#blocks
		#correlator = digital.correlate_access_code_bb(self._access_code, self._threshold)
		#framer_sink = gr.framer_sink_1(msgq)
		#initialize hier2
		gr.hier_block2.__init__(
			self,
			"packet_decoder",
			gr.io_signature(1, 1, gr.sizeof_char), # Input signature
			gr.io_signature(0, 0, 0) # Output signature
		)
		msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT) #holds packets from the PHY
		msg_sink = gr.message_sink(gr.sizeof_char, msgq, False) #False -> blocking
		#connect
		#self.connect(self, correlator, framer_sink)
		self.connect(self, msg_sink)
		#start thread
		_packet_decoder_thread(msgq, callback)

##################################################
## Packet Mod for OFDM Mod and Packet Encoder
##################################################
class packet_mod_base(gr.hier_block2):
	"""
	Hierarchical block for wrapping packet source block.
	"""

	def __init__(self, packet_source=None, payload_length=DEFAULT_PAYLOAD_LEN):

        #verify that packet length is a multiple of the stream size
		if payload_length%self._item_size_in != 0:	
			raise ValueError, 'The payload length: "%d" is not a mutiple of the stream size: "%d".'%(payload_length, self._item_size_in)

		#initialize hier2
		gr.hier_block2.__init__(
			self,
			"ofdm_mod",
			gr.io_signature(1, 1, self._item_size_in), # Input signature
			gr.io_signature(1, 1, packet_source._hb.output_signature().sizeof_stream_item(0)) # Output signature
		)
		#create blocks
		msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT)
		msg_sink = gr.message_sink(self._item_size_in, msgq, False) #False -> blocking
		#connect
		self.connect(self, msg_sink)
		self.connect(packet_source, self)
		#start thread
		_packet_encoder_thread(msgq, payload_length, packet_source.send_pkt)

class packet_mod_b(packet_mod_base): _item_size_in = gr.sizeof_char
class packet_mod_s(packet_mod_base): _item_size_in = gr.sizeof_short
class packet_mod_i(packet_mod_base): _item_size_in = gr.sizeof_int
class packet_mod_f(packet_mod_base): _item_size_in = gr.sizeof_float
class packet_mod_c(packet_mod_base): _item_size_in = gr.sizeof_gr_complex

##################################################
## Packet Demod for OFDM Demod and Packet Decoder
##################################################
class packet_demod_base(gr.hier_block2):
	"""
	Hierarchical block for wrapping packet sink block.
	"""

	old_state = ''
	state = "SEARCH"
	search_acc_string = ''
	# pack bytes
	local_byte = 0
	bit_counter = 0
	# pack packets
	local_packet = []
	byte_counter = 0

	def __init__(self, packet_sink=None, payload_length=DEFAULT_PAYLOAD_LEN):
		#initialize hier2
		self._packet_sink = packet_sink
		self._payload_length = payload_length
		gr.hier_block2.__init__(
			self,
			"ofdm_mod",
			gr.io_signature(1, 1, packet_sink._hb.input_signature().sizeof_stream_item(0)), # Input signature
			gr.io_signature(1, 1, self._item_size_out) # Output signature
		)
		#create blocks
		msg_source = gr.message_source(self._item_size_out, DEFAULT_MSGQ_LIMIT)
		self._msgq_out = msg_source.msgq()
		#connect
		self.connect(self, packet_sink)
		self.connect(msg_source, self)
		if packet_sink._hb.output_signature().sizeof_stream_item(0):
			self.connect(packet_sink, gr.null_sink(packet_sink._hb.output_signature().sizeof_stream_item(0)))

	def recv_pkt(self, ok, payload):
		#print payload, len(payload)
		data = self.get_data(payload, True)
		#print 'processed data: ', data
		string = ''
		for i in data:
			string += chr(i)
		msg = gr.message_from_string(string, 0, self._item_size_out, len(string)/self._item_size_out)
		if ok: self._msgq_out.insert_tail(msg)

	def get_data(self, payload, is_LSB):
		out = []
		access_code = self._packet_sink._access_code
		access_len = len(self._packet_sink._access_code)
		received_data_len = len(payload)
		packet_len = self._payload_length*8
		processed_packets = received_data_len//(packet_len)

		a = 0
		j = 0 # bit counter inside a byte
		nb_packet = 0

		#print 'received: ', received_data_len, 'bits'
		#print 'packet length:', packet_len, 'bits', self._payload_length, 'bytes'
		for i in range(len(payload)):
			v = False
			if self.old_state != self.state:
				v = True
				self.old_state = self.state

			self.search_acc_string = (self.search_acc_string + str(ord(payload[i])&0x1))[-access_len:]
			#print self.search_acc_string
			if self.state == "SEARCH":
				#if v:
					#print "SEARCH"
				#self.search_acc_string = (str(ord(payload[i])&0x1) + self.search_acc_string)[:access_len]
				if (self.search_acc_string == access_code):
					self.state = "SLICE_DATA"

			elif self.state == "SLICE_DATA":
				#if v:
					#print "FOUND ACCESS CODE", i
				new_byte = False
				new_packet = False

				(new_byte, byte) = self.pack_bytes(payload[i])
				if new_byte:
					#print 'new_byte: ', byte
					(new_packet, packet) = self.pack_packets(byte)
				if new_packet:
					#print 'new_packet: ', packet
					out += packet
					self.state = "SEARCH"
		return out

	def pack_bytes(self, bit):
		self.local_byte |= (ord(bit)&0x1)<<(7-self.bit_counter)
		#print self.bit_counter, self.local_byte
		if self.bit_counter == 7:
			self.bit_counter = 0
			byte = self.local_byte
			self.local_byte = 0
			return (True, byte)
		else:
			self.bit_counter += 1
			return (False, 0)

	def pack_packets(self, byte):
		self.local_packet += [byte]
		if self.byte_counter == self._payload_length-1:
			self.byte_counter = 0
			packet = self.local_packet
			self.local_packet = []
			return (True, packet)
		else:
			self.byte_counter += 1
			return (False, 0)

class packet_demod_b(packet_demod_base): _item_size_out = gr.sizeof_char
class packet_demod_s(packet_demod_base): _item_size_out = gr.sizeof_short
class packet_demod_i(packet_demod_base): _item_size_out = gr.sizeof_int
class packet_demod_f(packet_demod_base): _item_size_out = gr.sizeof_float
class packet_demod_c(packet_demod_base): _item_size_out = gr.sizeof_gr_complex
